/*
 * @lc app=leetcode.cn id=40 lang=cpp
 *
 * [40] 组合总和 II
 *
 * https://leetcode.cn/problems/combination-sum-ii/description/
 *
 * algorithms
 * Medium (59.52%)
 * Likes:    1513
 * Dislikes: 0
 * Total Accepted:    493.7K
 * Total Submissions: 829.4K
 * Testcase Example:  '[10,1,2,7,6,1,5]\n8'
 *
 * 给定一个候选人编号的集合 candidates 和一个目标数 target ，找出 candidates 中所有可以使数字和为 target 的组合。
 * 
 * candidates 中的每个数字在每个组合中只能使用 一次 。
 * 
 * 注意：解集不能包含重复的组合。 
 * 
 * 
 * 
 * 示例 1:
 * 
 * 
 * 输入: candidates = [10,1,2,7,6,1,5], target = 8,
 * 输出:
 * [
 * [1,1,6],
 * [1,2,5],
 * [1,7],
 * [2,6]
 * ]
 * 
 * 示例 2:
 * 
 * 
 * 输入: candidates = [2,5,2,1,2], target = 5,
 * 输出:
 * [
 * [1,2,2],
 * [5]
 * ]
 * 
 * 
 * 
 * 提示:
 * 
 * 
 * 1 <= candidates.length <= 100
 * 1 <= candidates[i] <= 50
 * 1 <= target <= 30
 * 
 * 
 */

// @lc code=start
class Solution {
public:
    void BackTracking(vector<vector<int>>& result, vector<int>& path, vector<bool>& used,
        const vector<int>& candidates, int target, int sum, int startIndex) {
        if (sum > target)
            return;

        if (sum == target) {
            result.push_back(path);
            return;
        }


        for (int i = startIndex; i < candidates.size(); i++) {
            //树层的去重, 即为组合之间去重
            //1.与前一个元素相同 candidates[i] == candidates[i - 1]
            //2.前一个元素未使用 !used[i - 1]
            //  used[i - 1] 为 true 时, 为树枝状态, 不需要去重
            if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1])
                continue;
            
            used[i] = true;
            path.push_back(candidates[i]);
            BackTracking(result, path, used, candidates, target, sum + candidates[i], i + 1);
            path.pop_back();
            used[i] = false;
        }
    }

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        if (candidates.size() == 0)
            return {};

        //使用 used 数组记录使用过的元素
        //[1, 1, 2]  target=3
        /*
                        112
            1-12(ynn)            1-2(nyn)       2-(nny)
        11-2(yyn)  12-(yny)     12-(nyy)      ....
            ...
        
        1.在这里我们观察第一层 (树层重复):
            1-12 和 1-2 必定会取到重复的集和(首元素都是1)

        2.再来看 1-12 这个树枝 (树枝重复):
            1-12 再取 1, 实际上是可以的, 因为集合中本身就有两个 1 元素

        所以本题的关键在于层中的去重
        */

        //组合去重, 先对元素进行排序
        std::sort(candidates.begin(), candidates.end());

        vector<vector<int>> result;
        vector<int> path;
        vector<bool> used(candidates.size(), false);

        BackTracking(result, path, used, candidates, target, 0, 0);

        return result;
    }
};
// @lc code=end

